﻿using System;
using System.Text.RegularExpressions;
using Kele.Activity.Models.Dto;
using Newtonsoft.Json.Linq;

namespace Kele.Activity
{
    public class ValidationRule
    {
        public string type { get; set; }
        public bool? required { get; set; }
        public string pattern { get; set; }
        public float? max { get; set; }
        public float? min { get; set; }
        public int? length { get; set; }

        // public Array @enum { get; set; }
        public bool? whitespace { get; set; }

        public string message { get; set; }
        public string validator { get; set; }
    }

    public class ActivityValidator
    {
        public string message { get; set; }

        public bool IsValid(string key, ActivityInputDto data, ValidationRule rule)
        {
            message = string.IsNullOrWhiteSpace(rule.message) ? "Validation error on field " + key : rule.message;
            var token = data.GetValue(key);

            //required
            //The required rule property indicates that the field must exist on the source object being validated.
            if ((rule.required ?? false) && token == null&& !token.HasValues) return false;
            //if (token == null) token = JToken.FromObject(string.Empty);

            //type
            if (!v_type(token, rule.type)) return false;

            //pattern
            //The pattern rule property indicates a regular expression that the value must match to pass validation
            if (!string.IsNullOrWhiteSpace(rule.pattern) && !v_pattern(token, rule.pattern)) return false;

            //length
            if (!v_length(token, rule.length)) return false;

            //min
            if (!v_min(token, rule.min)) return false;

            //max
            if (!v_max(token, rule.max)) return false;

            //whitespace
            if (rule.whitespace ?? false)
            {
                if (string.IsNullOrWhiteSpace(token.ToString())) return false;
            }

            //validator
            if (!v_validator(token, data, rule.validator, out var msg))
            {
                message = msg;
                return false;
            }
            message = string.Empty;
            return true;
        }

        private bool v_pattern(JToken token, string pattern)
        {
            return Regex.IsMatch(token.ToString(), pattern);
        }

        private bool v_type(JToken token, string type)
        {
            if (string.IsNullOrEmpty(type)) return true;
            switch (type)
            {
                case "string":
                    return token.Type == JTokenType.String;

                case "integer":
                    return token.Type == JTokenType.Integer;

                case "float":
                    return token.Type == JTokenType.Float;

                case "number":
                    return token.Type == JTokenType.Float || token.Type == JTokenType.Integer;

                case "boolean":
                    return token.Type == JTokenType.Boolean;

                case "array":
                    return token.Type == JTokenType.Array;

                case "date":
                    return token.Type == JTokenType.Date;

                case "email":
                    return v_pattern(token, @"^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$");

                case "url":
                    return v_pattern(token, @"^http://([\w-]+\.)+[\w-]+(/[\w-./?%&=]*)?$");

                case "object"://Must be of type object and not Array.isArray.
                    return token.Type == JTokenType.Object;

                case "any":
                default:// Can be any type.
                    return true;
            }
        }

        private bool v_length(JToken token, int? length)
        {
            if (length == null) return true;

            switch (token.Type)
            {
                case JTokenType.String:
                    return length == token.ToString().Length;

                case JTokenType.Array:
                    return token is JArray array ? length == array.Count : false;

                case JTokenType.Integer:
                    return length == token.Value<int>();

                default:
                    return true;
            }
        }

        private bool v_min(JToken token, float? min)
        {
            if (min == null) return true;
            switch (token.Type)
            {
                case JTokenType.String:
                    return min <= token.ToString().Length;

                case JTokenType.Array:
                    return token is JArray array && min <= array.Count;

                case JTokenType.Integer:
                    return min <= token.Value<int>();

                case JTokenType.Float:
                    return min <= token.Value<float>();

                default:
                    return true;
            }
        }

        private bool v_max(JToken token, float? max = 0)
        {
            if (max == null) return true;
            switch (token.Type)
            {
                case JTokenType.String:
                    return max >= token.ToString().Length;

                case JTokenType.Array:
                    return token is JArray array && max <= array.Count;

                case JTokenType.Integer:
                    return max >= token.Value<int>();

                case JTokenType.Float:
                    return max >= token.Value<float>();

                default:
                    return true;
            }
        }

        private bool v_validator(JToken token, ActivityInputDto data, string validator, out string message)
        {
            message = string.Empty;
            if (string.IsNullOrWhiteSpace(validator)) return true;
            switch (validator)
            {
                case "valid_start_time_end":

                    var start = data.StartTime ;
                    if (start == DateTime.MinValue)
                    {
                        message = "请设置活动开始和结束时间";
                        return false;
                    }
                    var end = data.EndTime;

                    if (end == DateTime.MinValue)
                    {
                        message = "请设置活动开始和结束时间";
                        return false;
                    }
                    if (start > end)
                    {
                        message = "活动时间设置有误,结束时间应该大于开始时间";
                        return false;
                    }
                    break;
            }
            return true;
        }
    }

    #region rule message

    //    private static readonly string xmessage = @"{
    //  'default': 'Validation error on field %s',
    //  'required': '%s is required',
    //  'enum': '%s must be one of %s',
    //  'whitespace': '%s cannot be empty',
    //  'date': {
    //      'format': '%s date %s is invalid for format %s',
    //      'parse': '%s date could not be parsed, %s is invalid ',
    //      'invalid': '%s date %s is invalid'
    //  },
    //  'types': {
    //      'string': '%s is not a %s',
    //      'method': '%s is not a %s (function)',
    //      'array': '%s is not an %s',
    //      'object': '%s is not an %s',
    //      'number': '%s is not a %s',
    //      'date': '%s is not a %s',
    //      'boolean': '%s is not a %s',
    //      'integer': '%s is not an %s',
    //      'float': '%s is not a %s',
    //      'regexp': '%s is not a valid %s',
    //      'email': '%s is not a valid %s',
    //      'url': '%s is not a valid %s',
    //      'hex': '%s is not a valid %s'
    //  },
    //  'string': {
    //      'len': '%s must be exactly %s characters',
    //      'min': '%s must be at least %s characters',
    //      'max': '%s cannot be longer than %s characters',
    //      'range': '%s must be between %s and %s characters'
    //  },
    //  'number': {
    //      'len': '%s must equal %s',
    //      'min': '%s cannot be less than %s',
    //      'max': '%s cannot be greater than %s',
    //      'range': '%s must be between %s and %s'
    //  },
    //  'array': {
    //      'len': '%s must be exactly %s in length',
    //      'min': '%s cannot be less than %s in length',
    //      'max': '%s cannot be greater than %s in length',
    //      'range': '%s must be between %s and %s in length'
    //  },
    //  'pattern': {
    //      'mismatch': '%s value %s does not match pattern %s'
    //  }
    //}";

    #endregion rule message
}